using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.ServiceModel;
using System.Threading;
using AutoMapper;
using Core;
using IglooCoder.Commons.WcfNhibernate;
using Infrastucture;
using Omu.ValueInjecter;
using WcfService1.DTO;

namespace WcfService1
{
    // NOTE: You can use the "Rename" command on the "Refactor" PurchaseInvoice to change the class name "PurchaseInvoiceServices" in code, svc and config file together.
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    [NHibernateContext]
    public class PurchaseInvoiceServices : IPurchaseInvoiceServices
    {
        private readonly PurchaseInvoiceRepository PurchaseInvoiceRepository;
        private readonly WcfSessionStorage _Session;
        private BTBRepository _btbRepository;
        private readonly CashBankOutDetailRepository _cashBankOutRepository;
        private readonly MutasiHutangRepository _mutasiHutangRepo;
        public PurchaseInvoiceServices()
        {
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
            _Session = new WcfSessionStorage();
            PurchaseInvoiceRepository = new PurchaseInvoiceRepository(_Session);
            _btbRepository = new BTBRepository(_Session);
            _cashBankOutRepository = new CashBankOutDetailRepository(_Session);
            _mutasiHutangRepo = new MutasiHutangRepository(_Session);
           
        }

        #region IPurchaseInvoiceServices Members

        public List<PurchaseInvoiceDTO> GetList(int page, out long recordCount, string sortProperty = null,
                                                string sortType = "ASC")
        {
            var PurchaseInvoices = new List<PurchaseInvoice>();
            var Results = new List<PurchaseInvoiceDTO>();

            if (string.IsNullOrEmpty(sortProperty))
            {
                PurchaseInvoices = PurchaseInvoiceRepository.GetDataPaged(page, 30, "PIDate", "DESC").ToList();
            }
            else
            {
                PurchaseInvoices = PurchaseInvoiceRepository.GetDataPaged(page, 30, sortProperty, sortType).ToList();
            }

            recordCount = PurchaseInvoiceRepository.RecordCount;

            foreach (var purchaseInvoice in PurchaseInvoices)
            {
                var purchaseInvoiceDTO = new PurchaseInvoiceDTO();
                purchaseInvoiceDTO.PIDate = purchaseInvoice.PIDate;
                purchaseInvoiceDTO.PINo = purchaseInvoice.PINo;
                purchaseInvoiceDTO.Description = purchaseInvoice.Description;
                purchaseInvoiceDTO.Term = purchaseInvoice.Term;
                purchaseInvoiceDTO.Discount = purchaseInvoice.Discount;
                purchaseInvoiceDTO.DiscountType = purchaseInvoice.DiscountType;
                purchaseInvoiceDTO.Supplier = new SupplierDTO() {SupplierId = purchaseInvoice.Supplier.SupplierId,SupplierName = purchaseInvoice.Supplier.SupplierName};
                purchaseInvoiceDTO.Kurs = purchaseInvoice.Kurs;
                purchaseInvoiceDTO.Currency = new CurrencyDTO()
                                                  {
                                                      CurrencyCode = purchaseInvoice.Currency.CurrencyCode,
                                                      CurrencyName = purchaseInvoice.Currency.CurrencyName
                                                  };
                purchaseInvoiceDTO.Ppn = purchaseInvoice.Ppn;
                if (purchaseInvoice.Btbs != null)
                {
                    foreach (var btb in purchaseInvoice.Btbs)
                    {
                        purchaseInvoiceDTO.Btbs.Add(new BTBDTO(){NoBtb = btb.NoBtb});
                        purchaseInvoiceDTO.NoPO = btb.PurchaseOrder.PONumber;
                    }
                   
                }
                Results.Add(purchaseInvoiceDTO);
            }

            return Results;
        }

        public bool Save(PurchaseInvoiceDTO PurchaseInvoiceDTO)
        {
            bool res;
            try
            {
                PurchaseInvoice PurchaseInvoice =
                    PurchaseInvoiceRepository.GetDataOne(x => x.PINo == PurchaseInvoiceDTO.PINo);

                if (PurchaseInvoice == null)
                {
                    PurchaseInvoice = new PurchaseInvoice();
                    Mapper.Map(PurchaseInvoiceDTO, PurchaseInvoice);
                    //Untuk New entry dari transaksi bukan dari email
                    if (String.IsNullOrEmpty(PurchaseInvoice.PINo))
                             PurchaseInvoice.PINo = GetLastPINo();
                    foreach (PurchaseInvoiceDetail purchaseInvoiceDetail in PurchaseInvoice.PurchaseInvoiceDetails)
                    {
                        purchaseInvoiceDetail.PurchaseInvoice = PurchaseInvoice;
                        purchaseInvoiceDetail.PurchaseInvoiceDetailId = 0;
                        if (purchaseInvoiceDetail.DiscountType == null)
                            purchaseInvoiceDetail.DiscountType = "%";
                    }

                    PurchaseInvoiceRepository.ExecuteTransactionNative(() =>
                    {
                                    PurchaseInvoiceRepository.SaveOrUpdate(
                                        PurchaseInvoice);
                                    //Update Inventory Price
                                    var inventoryRepository =
                                        new InventoryRepository(_Session);
                                    foreach (PurchaseInvoiceDetail
                                            purchaseInvoiceDetail in
                                            PurchaseInvoice.
                                                PurchaseInvoiceDetails)
                                    {
                                        Inventory inventory = purchaseInvoiceDetail.Inventory;
                                        inventory.UnitPrice =
                                            purchaseInvoiceDetail.Price +
                                            purchaseInvoiceDetail.BiayaImport +
                                            purchaseInvoiceDetail.BiayaForwarder;
                                        inventoryRepository.UpdateUnitPrice(inventory);
                                    }

                                    foreach (var btb in PurchaseInvoiceDTO.Btbs)
                                    {
                                        _btbRepository.UpdatePurchaseInvoice(btb.NoBtb, PurchaseInvoice.PINo);
                                    }
                      
                    });
                }else
                {
                    PurchaseInvoiceRepository.ExecuteTransactionNative(()=>
                                                                           {
                                                                               //Jika Status Void dan Close tdk boleh Update
                                                                               if (PurchaseInvoice.Status == (int)Status.Void || PurchaseInvoice.Status == (int)Status.Close)
                                                                                   throw new Exception("Tidak bisa di Update Status sudah Close atau Void!");

                                                                               PurchaseInvoiceRepository.Delete(PurchaseInvoice);
                                                                               foreach (var btb in PurchaseInvoice.Btbs)
                                                                               {
                                                                                   btb.Status = 0;
                                                                                   _btbRepository.SaveOrUpdate(btb);
                                                                               }
                                                                               var PI = new PurchaseInvoice();
                                                                               PI.modified_date = DateTime.Now;
                                                                               PI.created_date = PurchaseInvoice.created_date;
                                                                               Mapper.Map(PurchaseInvoiceDTO, PI);
                                                                               foreach (var piDetail in PI.PurchaseInvoiceDetails)
                                                                               {
                                                                                   piDetail.PurchaseInvoice = PI;
                                                                                   if (piDetail.DiscountType == null)
                                                                                       piDetail.DiscountType = "%";
                                                                               }
                                                                               PurchaseInvoiceRepository.Session.SaveOrUpdateCopy(PI);
                                                                               //Update Inventory Price
                                                                               var inventoryRepository =
                                                                                   new InventoryRepository(_Session);
                                                                               foreach (PurchaseInvoiceDetail
                                                                                           purchaseInvoiceDetail in
                                                                                           PI.PurchaseInvoiceDetails)
                                                                               {
                                                                                   Inventory inventory = purchaseInvoiceDetail.Inventory;
                                                                                   inventory.UnitPrice =
                                                                                       purchaseInvoiceDetail.Price +
                                                                                       purchaseInvoiceDetail.BiayaImport +
                                                                                       purchaseInvoiceDetail.BiayaForwarder;
                                                                                   inventoryRepository.UpdateUnitPrice(inventory);
                                                                               }

                                                                               foreach (var btb in PurchaseInvoiceDTO.Btbs)
                                                                               {
                                                                                   _btbRepository.UpdatePurchaseInvoice(btb.NoBtb, PurchaseInvoice.PINo);
                                                                               }
                                                                             
                                                                           });
                    
                }

            

                res = true;
            }
            catch (Exception)
            {
                res = false;
                throw;
            }

            return res;
        }


        public bool Delete(PurchaseInvoiceDTO PurchaseInvoiceDto)
        {
            bool res;
            try
            {
                PurchaseInvoice PurchaseInvoice =
                    PurchaseInvoiceRepository.GetDataOne(x => x.PINo == PurchaseInvoiceDto.PINo);
                //PurchaseInvoiceRepository.Delete(PurchaseInvoice);
                PurchaseInvoice.Status = 2;
                PurchaseInvoiceRepository.SaveOrUpdate(PurchaseInvoice);
                res = true;
            }
            catch (Exception)
            {
                res = false;
            }

            return res;
        }


        public List<PurchaseInvoiceDTO> GetListByCriteria(Dictionary<String, Object> criteria, string expression,
                                                          int page, out long recordCount)
        {
            var PurchaseInvoices = new List<PurchaseInvoice>();
            var Results = new List<PurchaseInvoiceDTO>();
            PurchaseInvoices = PurchaseInvoiceRepository.GetDataPaged(criteria, expression, page, 30).ToList();
            recordCount = PurchaseInvoiceRepository.RecordCount;
            foreach (var purchaseInvoice in PurchaseInvoices)
            {
                var purchaseInvoiceDTO = new PurchaseInvoiceDTO();
                purchaseInvoiceDTO.PIDate = purchaseInvoice.PIDate;
                purchaseInvoiceDTO.PINo = purchaseInvoice.PINo;
                purchaseInvoiceDTO.Description = purchaseInvoice.Description;
                purchaseInvoiceDTO.Term = purchaseInvoice.Term;
                purchaseInvoiceDTO.Discount = purchaseInvoice.Discount;
                purchaseInvoiceDTO.DiscountType = purchaseInvoice.DiscountType;
                purchaseInvoiceDTO.Supplier = new SupplierDTO() {SupplierId = purchaseInvoice.Supplier.SupplierId,SupplierName = purchaseInvoice.Supplier.SupplierName};
                purchaseInvoiceDTO.Kurs = purchaseInvoice.Kurs;
                purchaseInvoiceDTO.Currency = new CurrencyDTO()
                                                  {
                                                      CurrencyCode = purchaseInvoice.Currency.CurrencyCode,
                                                      CurrencyName = purchaseInvoice.Currency.CurrencyName
                                                  };
                purchaseInvoiceDTO.Ppn = purchaseInvoice.Ppn;
                if (purchaseInvoice.Btbs != null)
                {
                    foreach (var btb in purchaseInvoice.Btbs)
                    {
                        purchaseInvoiceDTO.Btbs.Add(new BTBDTO(){NoBtb = btb.NoBtb});
                        purchaseInvoiceDTO.NoPO = btb.PurchaseOrder.PONumber;
                    }
                   
                }
                Results.Add(purchaseInvoiceDTO);
            }

            return Results;
        }


        public List<PurchaseInvoiceDTO> GetAllPurchaseInvoice()
        {
            List<PurchaseInvoice> PurchaseInvoices = PurchaseInvoiceRepository.FetchAll().ToList();
            var Results = new List<PurchaseInvoiceDTO>();
            foreach (var purchaseInvoice in PurchaseInvoices)
            {
                var purchaseInvoiceDTO = new PurchaseInvoiceDTO();
                purchaseInvoiceDTO.PIDate = purchaseInvoice.PIDate;
                purchaseInvoiceDTO.PINo = purchaseInvoice.PINo;
                purchaseInvoiceDTO.Description = purchaseInvoice.Description;
                purchaseInvoiceDTO.Term = purchaseInvoice.Term;
                purchaseInvoiceDTO.Discount = purchaseInvoice.Discount;
                purchaseInvoiceDTO.DiscountType = purchaseInvoice.DiscountType;
                purchaseInvoiceDTO.Supplier = purchaseInvoice.Supplier.ConvertToDTO<Supplier, SupplierDTO>();
                purchaseInvoiceDTO.Kurs = purchaseInvoice.Kurs;
                purchaseInvoiceDTO.Currency = purchaseInvoice.Currency.ConvertToDTO<Currency, CurrencyDTO>();
                purchaseInvoiceDTO.Ppn = purchaseInvoice.Ppn;
                if (purchaseInvoice.Btbs != null)
                {
                    foreach (var btb in purchaseInvoice.Btbs)
                    {
                        purchaseInvoiceDTO.Btbs.Add(new BTBDTO() { NoBtb = btb.NoBtb });
                        purchaseInvoiceDTO.NoPO = btb.PurchaseOrder.PONumber;
                    }

                }
                Results.Add(purchaseInvoiceDTO);
            }

            return Results;
        }


        public PurchaseInvoiceDTO GetPurchaseInvoice(String kodePurchaseInvoice)
        {
            PurchaseInvoice PurchaseInvoice = PurchaseInvoiceRepository.GetDataOne(x => x.PINo == kodePurchaseInvoice);
            var PurchaseInvoiceDto = new PurchaseInvoiceDTO();
            PurchaseInvoiceDto.InjectFrom<CloneInjection>(PurchaseInvoice);
            return PurchaseInvoiceDto;
        }

        public List<PurchaseInvoiceDTO> GetPurchaseInvoiceList(string kodePurchaseInvoice)
        {
            List<PurchaseInvoice> PurchaseInvoices =
                PurchaseInvoiceRepository.GetDataList(x => x.PINo.Contains(kodePurchaseInvoice)).ToList();
            var Results = new List<PurchaseInvoiceDTO>();
            PurchaseInvoices.ForEach(x =>
                                         {
                                             var PurchaseInvoiceDto = new PurchaseInvoiceDTO();
                                             Mapper.Map(x, PurchaseInvoiceDto);
                                             Results.Add(PurchaseInvoiceDto);
                                         }
                );

            return Results;
        }

        public string GetLastPINo()
        {
            var appParamServices = new AppParamServices();
            AppParamDTO param = appParamServices.GetAppParamList("KodePI").FirstOrDefault();
            string res = PurchaseInvoiceRepository.GetMaxNumber(param.AppParamValue);
           
            if (string.IsNullOrEmpty(res))
                res = param.AppParamValue;

            string formatSO = "0000";
            string kodeSO;
            string lastNo = string.Empty;
            if (res.Length <= 4)
            {
                lastNo = formatSO;
                kodeSO = res;
            }
            else
            {
                lastNo = res.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries)[2];
                kodeSO = res.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries)[0];
            }

            string soNum = formatSO.Substring(0, formatSO.Length - (int.Parse(lastNo) + 1).ToString().Length) +
                           (int.Parse(lastNo) + 1);
            return kodeSO + "/" + DateTime.Now.Year + "/" + soNum;
        }

        public bool DeleteDetail(PurchaseInvoiceDetailDTO purchaseInvoiceDetailDto)
        {
            bool res;
            try
            {
                var PIDetail =
                    PurchaseInvoiceRepository.Session.Get<PurchaseInvoiceDetail>(
                        purchaseInvoiceDetailDto.PurchaseInvoiceDetailId);
                PurchaseInvoiceRepository.DeleteDetail(PIDetail, purchaseInvoiceDetailDto.PurchaseInvoiceDetailId);
                res = true;
            }
            catch (Exception)
            {
                res = false;
            }

            return res;
        }

        public void UpdateStatus(PurchaseInvoiceDTO purchaseInvoiceDto, Status status)
        {
            PurchaseInvoiceRepository.UpdateStatus(purchaseInvoiceDto.PINo, (int)status);
        }

        public IEnumerable<PurchaseInvoiceDTO>  GetPurchaseInvoiceBySupplier(SupplierDTO supplier)
        {
            List<MutasiHutang> pis = new List<MutasiHutang>();

            if (supplier == null)
                pis = _mutasiHutangRepo.GetHutangBelumLunas();
            else
                pis = _mutasiHutangRepo.GetHutangBelumLunas(supplier.SupplierId);

            var results = new List<PurchaseInvoiceDTO>();
            foreach (var pi in pis)
            {
                var piDTO = new PurchaseInvoiceDTO();
                piDTO.PINo = pi.InvoiceNo;
                piDTO.Total = pi.SaldoSisa;
                results.Add(piDTO);
            }
            return results;
        }
 


         public void CreateJurnal(PurchaseInvoice purchaseInvoice)
         {
             var _jurnalRepository = new JurnalRepository(_Session);
            //Jurnal Hutang
            Jurnal jurnal = new Jurnal();
            jurnal.NoVoucher = purchaseInvoice.PINo;
            jurnal.TglJurnal = purchaseInvoice.PIDate;
            jurnal.Description = purchaseInvoice.Description;
            jurnal.ReffNumber = purchaseInvoice.PINo;
            jurnal.TipeJurnal = typeof(PurchaseInvoice).Name;

            
            decimal afterDisc = 0;
            if (purchaseInvoice.DiscountType == "%")
                afterDisc = purchaseInvoice.PurchaseInvoiceDetails.Sum(x => x.TotalDetail) -
                            (purchaseInvoice.PurchaseInvoiceDetails.Sum(x => x.TotalDetail)*purchaseInvoice.Discount/100);
            else
                afterDisc = purchaseInvoice.PurchaseInvoiceDetails.Sum(x => x.TotalDetail) - purchaseInvoice.Discount;

            foreach (var invoiceDetail in purchaseInvoice.PurchaseInvoiceDetails)
            {
                //inventory
                if (string.IsNullOrEmpty(invoiceDetail.Inventory.Item.CoaInventory)) throw new Exception(invoiceDetail.Inventory.SerialNumber + " belum memiliki chart of account inventory!");
                jurnal.JurnalDetails.Add(new JurnalDetail() { AccountNumber = invoiceDetail.Inventory.Item.CoaInventory, Debit = invoiceDetail.TotalDetail, Jurnal = jurnal, Description = invoiceDetail.Inventory.Item.ItemId + ":" + invoiceDetail.Inventory.Item.CoaInventory });
            }

            List<JurnalDetail> results = new List<JurnalDetail>();
            //Hutang 
            if (purchaseInvoice.Supplier.Coa == null) throw new Exception(purchaseInvoice.Supplier.SupplierName + " belum memiliki chart of account!");
            results.Add(new JurnalDetail() { AccountNumber = purchaseInvoice.Supplier.Coa.AccountNumber, Kredit = purchaseInvoice.Ppn == 0 ? afterDisc : afterDisc + purchaseInvoice.Ppn, Jurnal = jurnal });
          
            foreach (var gjd in jurnal.JurnalDetails.GroupBy(x => x.Description))
            {
                results.Add(new JurnalDetail()
                {
                    AccountNumber = jurnal.JurnalDetails.FirstOrDefault(x=> x.Description == gjd.Key).AccountNumber,
                    Debit = jurnal.JurnalDetails.Where(x => x.Description == gjd.Key).Sum(x=>x.Debit),
                    Kredit = jurnal.JurnalDetails.Where(x => x.Description == gjd.Key).Sum(x => x.Kredit),
                    Departemen = jurnal.JurnalDetails.FirstOrDefault(x => x.Description == gjd.Key).Departemen,
                    Description = jurnal.JurnalDetails.FirstOrDefault(x => x.Description == gjd.Key).Description,
                    Jurnal = jurnal
                });
            }
            if (purchaseInvoice.Ppn > 0 )
            {
                var _coaRepository = new CoaRepository(_Session);
                var coaPPn = _coaRepository.GetDataOne(x => x.AccountName.Contains("ppn"));
                if (coaPPn == null)throw  new Exception("Chart of Account PPN belum ada!");
                results.Add(new JurnalDetail() { AccountNumber = coaPPn.AccountNumber, Kredit = purchaseInvoice.Ppn, Jurnal = jurnal });
            }
            jurnal.JurnalDetails = results;
            _jurnalRepository.SaveOrUpdate(jurnal);


            //Kartu Hutang
            var mutasiHutangRepo = new MutasiHutangRepository(_Session);
            var lastmutasi = mutasiHutangRepo.FindLastMutasi(purchaseInvoice.Supplier.SupplierId, purchaseInvoice.PINo,
                                                              purchaseInvoice.PIDate);
            var mutasiHutang = new MutasiHutang();
            mutasiHutang.SaldoAwal = lastmutasi.SaldoSisa;
            mutasiHutang.SaldoMasuk = purchaseInvoice.Ppn == 0 ? afterDisc : afterDisc + purchaseInvoice.Ppn;
            mutasiHutang.SaldoSisa = mutasiHutang.SaldoAwal + mutasiHutang.SaldoMasuk;
            mutasiHutang.SupplierId = purchaseInvoice.Supplier.SupplierId;
            mutasiHutang.Tanggal = purchaseInvoice.PIDate;
            mutasiHutang.Type = typeof(PurchaseInvoice).Name;
            mutasiHutang.InvoiceNo = purchaseInvoice.PINo;
            mutasiHutang.Coa = purchaseInvoice.Supplier.Coa.AccountNumber;
            mutasiHutang.ReferenceNumber = purchaseInvoice.PINo;
            mutasiHutangRepo.SaveOrUpdate(mutasiHutang);
        }


        public void Posting(IEnumerable<PurchaseInvoiceDTO> purchaseInvoiceDtos)
        {
            PurchaseInvoiceRepository.ExecuteTransactionNative(() =>
            {
                foreach (var purchaseInvoiceDto in purchaseInvoiceDtos)
                {
                    var pi = PurchaseInvoiceRepository.GetDataOne(x => x.PINo == purchaseInvoiceDto.PINo);
                    PurchaseInvoiceRepository.UpdateStatus(purchaseInvoiceDto.PINo, 3);
                    CreateJurnal(pi);
                }
            });

        }


        public void UnPosting(IEnumerable<PurchaseInvoiceDTO> purchaseInvoiceDtos)
        {
            var _jurnalRepository = new JurnalRepository(_Session);
            var _hppRepository = new HppRepository(_Session);
            PurchaseInvoiceRepository.ExecuteTransactionNative(() =>
            {
                foreach (var purchaseInvoiceDto in purchaseInvoiceDtos)
                {
                    var purchaseInvoice = PurchaseInvoiceRepository.GetDataOne(x => x.PINo == purchaseInvoiceDto.PINo);
                    PurchaseInvoiceRepository.UpdateStatus(purchaseInvoice.PINo, 1);
                    var jurnal = _jurnalRepository.GetDataOne(x => x.NoVoucher == purchaseInvoiceDto.PINo);
                    _jurnalRepository.Delete(jurnal);
                    _hppRepository.DeleteHpp(purchaseInvoiceDto.PINo);
                    var piutangReposioty = new MutasiPiutangRepository(_Session);
                    piutangReposioty.DeleteMutasiPiutang(purchaseInvoiceDto.PINo);
                }
            });

        }


           public IEnumerable<PurchaseInvoiceDTO> GetListByPeriode(DateTime tglstart, DateTime tglend)
        {
            List<PurchaseInvoice> doElses = PurchaseInvoiceRepository.GetDataList(x => x.PIDate >= tglstart && x.PIDate <= tglend).ToList();
            var results = new List<PurchaseInvoiceDTO>();

            foreach (var doElse in doElses)
            {
                var doElseDTO = new PurchaseInvoiceDTO();
                doElseDTO.InjectFrom(doElse);
                results.Add(doElseDTO);
            }
            return results;
        }

        #endregion
    }

      
}